﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace NetfractionHub.Network.Protocol
{
	[global::System.AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	sealed class ProtocolFeatureAttribute : Attribute
	{
		public ProtocolFeatureAttribute()
		{
		}

		public string Name { get; set; }
	}

	public abstract class ProtocolFeature
	{
		private Dictionary<ProtocolFeatureAction, ProtocolMessageActionHandler> _protocolMessageActionHandlers;
		private List<ProtocolFeatureAction> _supportedActions;
		
		protected ProtocolFeature()
		{
			_protocolMessageActionHandlers = new Dictionary<ProtocolFeatureAction, ProtocolMessageActionHandler>();
			_supportedActions = new List<ProtocolFeatureAction>();
		}

		public static string GetFeatureName(Type type)
		{
			if (type.BaseType == typeof(Protocol.ProtocolFeature) && !type.IsAbstract)
			{
				object[] attribs = type.GetCustomAttributes(typeof(Protocol.ProtocolFeatureAttribute), false);
				if (attribs != null && attribs.Length > 0)
					return (attribs[0] as Protocol.ProtocolFeatureAttribute).Name;
			}
			return string.Empty;
		}

		public static string GetFeatureName(ProtocolFeature feature)
		{
			return GetFeatureName(feature.GetType());
		}

		/// <summary>
		/// Get a dictionary of ProtocolMessageHandlers
		/// </summary>
		protected Dictionary<ProtocolFeatureAction, ProtocolMessageActionHandler> ProtocolMessageHandlers
		{
			get { return _protocolMessageActionHandlers; }
		}

		public virtual void HandleFeatureMessage(RemoteMachine.Node node, ProtocolMessage message, 
												IEnumerable<ProtocolFeature> extensionFeatures)
		{
			var a = from p in this.ProtocolMessageHandlers
					where p.Key.ActionCode == message.MessageCode
					select p;
			if (a.Count() > 0)
			{
				bool handlerResult;

				ProtocolMessageActionHandler handler = a.First().Value;

				handlerResult = handler.HandleProtocolMessage(node, message, extensionFeatures);
			}
			/*ProtocolFeatureAction pfa = new ProtocolFeatureAction(message.MessageCode, FeatureActionType.Any);
			if (this.ProtocolMessageHandlers.ContainsKey(pfa))
			{
				bool handlerResult;

				if (this.ProtocolMessageHandlers[pfa] != null)
				{
					ProtocolMessageActionHandler handler = this.ProtocolMessageHandlers[pfa];

					handlerResult = handler.HandleProtocolMessage(node, message, extensionFeatures);
				}
			}*/
		}

		public virtual object HandleExtensionMessage(RemoteMachine.Node node, ProtocolMessage message, int extensionType, params object[] args)
		{
			object returnValue = null;
			var a = from p in this.ProtocolMessageHandlers
					where (GetAttribute<ProtocolMessageActionHandler, MessageExtensionAttribute>(p.Value, new MessageExtensionAttribute())).MessageName == message.MessageCode
					&& (GetAttribute<ProtocolMessageActionHandler, MessageExtensionAttribute>(p.Value, new MessageExtensionAttribute())).ExtensionType == extensionType
					select p;
			if (a.Count() > 0)
			{
				ProtocolMessageActionHandler handler = a.First().Value;
				returnValue = handler.HandleMessageExtension(node, message, args);
			}
			return returnValue;
		}

		protected static U GetAttribute<T, U>(T t, U u) where U : System.Attribute
		{
			return t.GetType().GetCustomAttributes(u.GetType(), false)[0] as U;
		}

		/// <summary>
		/// Called when a Node connects
		/// </summary>     
		/// <returns>True if the ProtocolFeature handled the event; otherwise false</returns>
		public virtual bool HandleConnect(RemoteMachine.Node node)
		{
			return false;
		}

		/// <summary>
		/// Called when a Node disconnects
		/// </summary>        
		/// <returns>True if the ProtocolFeature handled the event; otherwise false</returns>
		public virtual bool HandleDisconnect(RemoteMachine.Node node)
		{
			return true;
		}

		public List<ProtocolFeatureAction> SupportedActions
		{
			get
			{
				return _supportedActions;
			}
		}

		protected static List<ProtocolFeatureAction> featureActionsCache;

		/// <summary>
		/// Obtains the Actions a Feature supports via the const string fields in a class/struct passed to it.
		/// </summary>
		/// <parameter name="featureActions">A class or struct containing a set of const string fields.</parameter>
		/// <returns>A list of the Action protocol codes.</returns>
		protected List<ProtocolFeatureAction> GetFeatureActions(object featureActions)
		{
			if (featureActionsCache != null)
				return featureActionsCache;

			List<ProtocolFeatureAction> actions = new List<ProtocolFeatureAction>();

			foreach (System.Reflection.FieldInfo field in featureActions.GetType().GetFields())
			{
				actions.Add(new ProtocolFeatureAction(field.GetValue(null) as string,
					(field.GetCustomAttributes(typeof(ProtocolFeatureActionAttribute), false)[0] as ProtocolFeatureActionAttribute).ActionType));
			}

			featureActionsCache = actions;
			return actions;
		}

		public bool SupportsAction(string messageCode)
		{
			var res = from p in _supportedActions
					  where p.ActionCode == messageCode
					  select p;
			return res.Count() > 0;
		}

		public bool SupportsActionOfType(string messageCode, FeatureActionType actionType)
		{
			var res = from p in _supportedActions
					  where p.ActionCode == messageCode && p.ActionType == actionType
					  select p;
			return res.Count() > 0;
		}
	}

}
